import * as mongoose from 'mongoose';

type PaginateReturn<T> = {
  list: Array<T>;
  total: number;
  totalPages: number;
  page: number;
  pageSize: number;
};

type Paginate<T = any> = {
  paginate?: (options: {
    query: any;
    field: any;
    page?: number;
    limit?: number;
    sort?: unknown;
    populate?: { path: string | any; select?: string | any }[];
  }) => PaginateReturn<T>;
};

export type ModelPaginate<T extends mongoose.Document> = {
  [k in keyof mongoose.Model<T>]: mongoose.Model<T>[k];
} &
  Paginate<T>;

export default (schema: mongoose.Schema): mongoose.Schema => {
  async function paginate<T = any>(options: {
    query: any;
    field: any;
    page: number;
    limit: number;
    sort: unknown;
    populate: { path: string | any; select?: string | any }[];
  }) {
    const { page, limit, sort, query = {}, field = {} } = options;
    let $FD = this.find(query, field, {
      skip: (page - 1) * limit,
      limit: limit,
      sort: sort,
    });
    if (Array.isArray(options.populate)) {
      options.populate.forEach((item) => {
        $FD = $FD.populate(item.path, item.select);
      });
    }
    const [list, total] = await Promise.all([$FD, this.countDocuments(query)]);
    return {
      list,
      total,
      page,
      totalPages: Math.ceil(total / limit),
      pageSize: limit,
    } as PaginateReturn<T>;
  }
  schema.statics.paginate = paginate;

  return schema;
};
